/******************************************************************************
* @file spiFlash.c
* @author Zohar
* @version 1.00
* @date 2019.09.21
* @brief
******************************************************************************/
#include "spiFlash.h"
#include "stdbool.h"

//ID
#define sFLASH_ID 0xC84015 //  0XEF4017    //W25Q64  GD
//命令
#define W25X_WriteEnable 0x06
#define W25X_WriteDisable 0x04
#define W25X_ReadStatusReg 0x05
#define W25X_WriteStatusReg 0x01
#define W25X_ReadData 0x03
#define W25X_FastReadData 0x0B
#define W25X_FastReadDual 0x3B
#define W25X_PageProgram 0x02
#define W25X_BlockErase 0xD8
#define W25X_SectorErase 0x20
#define W25X_ChipErase 0xC7
#define W25X_PowerDown 0xB9
#define W25X_ReleasePowerDown 0xAB
#define W25X_DeviceID 0xAB
#define W25X_ManufactDeviceID 0x90
#define W25X_JedecDeviceID 0x9F
//标志
#define WIP_Flag 0x01 /* Write In Progress (WIP) flag */
//空字符
#define Dummy_Byte 0xFF

//W25Q16 flash大小 16M bit
#define SPI_FLASH_BIT_SIZE 16 * 1024 * 1024
#define SPI_FLASH_BYTE_SIZE 2 * 1024 * 1024

#define SPI_FLASH_PAGE_SIZE 256        //扇区大小,单位byte
#define SPI_FLASH_BLOCK_SIZE 64        //块大小，单位Kbyte,也可32K,一般不用32K
#define SPI_FLASH_BLOCK_COUNT 32       //块个数,根据块大小64K计算
#define SPI_FLASH_SECTOR_SIZE 4096     //扇区大小，单位byte
#define SPI_FLASH_SECTOR_COUNT = 512;  //扇区个数 512*4096=2M
#define SPI_FLASH_PerWritePageSize 256 //每次写入大小

#define SPI_FLASH_CS_LOW HAL_GPIO_WritePin(SPI2_NSS_GPIO_Port, SPI2_NSS_Pin, GPIO_PIN_RESET) //输出低电平
#define SPI_FLASH_CS_HIGH HAL_GPIO_WritePin(SPI2_NSS_GPIO_Port, SPI2_NSS_Pin, GPIO_PIN_SET)  //设置为高电平

void SPI_FLASH_WriteEnable(void)
{

#define TIME_OUT 1000 //单位ms

    uint8_t TxBuf[4] = {0, 0, 0, 0};
    uint8_t *pTxBuf = TxBuf;

    SPI_FLASH_CS_LOW;

    *pTxBuf++ = W25X_WriteEnable;

    HAL_SPI_Transmit(&hspi2, TxBuf, 1, TIME_OUT);

    SPI_FLASH_CS_HIGH;

#undef TIME_OUT
}

void SPI_FLASH_WaitForWriteEnd(void)
{
#define TIME_OUT 1000 //单位ms

    uint8_t TxBuf[4] = {0, 0, 0, 0};
    uint8_t RxBuf[4] = {0, 0, 0, 0};
    uint8_t *pTxBuf = TxBuf;

    SPI_FLASH_CS_LOW;

    *pTxBuf++ = W25X_ReadStatusReg;

    HAL_SPI_Transmit(&hspi2, TxBuf, 1, TIME_OUT);

    do
    {
        HAL_SPI_Receive(&hspi2, RxBuf, 1, TIME_OUT);

    } while ((RxBuf[0] & WIP_Flag) == 1); /* Write in progress */

    /* Deselect the FLASH: Chip Select high */
    SPI_FLASH_CS_HIGH;

#undef TIME_OUT
}

void SPI_FLASH_SectorErase(uint32_t Addr)
{
#define TIME_OUT 1000 //单位ms

    uint8_t TxBuf[4] = {0, 0, 0, 0};
    uint8_t *pTxBuf = TxBuf;

    SPI_FLASH_WriteEnable();

    SPI_FLASH_WaitForWriteEnd();

    SPI_FLASH_CS_LOW;

    *pTxBuf++ = W25X_SectorErase;
    *pTxBuf++ = (Addr >> 16) & 0xff;
    *pTxBuf++ = (Addr >> 8) & 0xff;
    *pTxBuf++ = (Addr >> 0) & 0xff;

    HAL_SPI_Transmit(&hspi2, TxBuf, 4, TIME_OUT);

    SPI_FLASH_CS_HIGH;

    SPI_FLASH_WaitForWriteEnd();

#undef TIME_OUT
}

void SPI_FLASH_ChipErase(void)
{
#define TIME_OUT 1000 //单位ms

    uint8_t TxBuf[4] = {0, 0, 0, 0};
    uint8_t *pTxBuf = TxBuf;

    SPI_FLASH_WriteEnable();

    SPI_FLASH_WaitForWriteEnd();

    SPI_FLASH_CS_LOW;

    *pTxBuf++ = W25X_ChipErase;

    HAL_SPI_Transmit(&hspi2, TxBuf, 1, TIME_OUT);

    SPI_FLASH_CS_HIGH;

    SPI_FLASH_WaitForWriteEnd();

#undef TIME_OUT
}

void SPI_FLASH_PageWrite(uint8_t *pBuffer, uint32_t Addr, uint16_t Num)

{
#define TIME_OUT 1000 //单位ms

    if (!Num)
    {
        return;
    }

    uint8_t TxBuf[4] = {0, 0, 0, 0};
    uint8_t *pTxBuf = TxBuf;

    SPI_FLASH_WriteEnable();

    SPI_FLASH_CS_LOW;

    *pTxBuf++ = W25X_PageProgram;
    *pTxBuf++ = (Addr >> 16) & 0xff;
    *pTxBuf++ = (Addr >> 8) & 0xff;
    *pTxBuf++ = (Addr >> 0) & 0xff;

    if (Num > SPI_FLASH_PerWritePageSize) //只写一页
    {
        Num = SPI_FLASH_PerWritePageSize;
    }

    HAL_SPI_Transmit(&hspi2, TxBuf, 4, TIME_OUT);

    HAL_SPI_Transmit(&hspi2, pBuffer, Num, TIME_OUT);

    SPI_FLASH_CS_HIGH;

    /* Wait the end of Flash writing */
    SPI_FLASH_WaitForWriteEnd();

#undef TIME_OUT
}

//要求地址必须为页的起始地址，切数据为整页数
void SPI_FLASH_BufferWrite(uint8_t *pBuffer, uint32_t Addr, uint16_t Num)
{
    //要发送数据占的整页数（每页256byte），不足整页的定义为下一个变量
    uint8_t NumOfPage = Num / SPI_FLASH_PAGE_SIZE;
    //要发送数据不足整页的数据数量
    uint8_t NumOfSingle = Num % SPI_FLASH_PAGE_SIZE;

    if (NumOfPage == 0) //发送的字符数小于256，不足一页
    {
        SPI_FLASH_PageWrite(pBuffer, Addr, Num);
    }
    else /* NumByteToWrite > SPI_FLASH_PAGE_SIZE */ //发送的字符数大于256，大于一页
    {
        while (NumOfPage--) // 写整页的数据
        {
            SPI_FLASH_PageWrite(pBuffer, Addr, SPI_FLASH_PAGE_SIZE);
            Addr += SPI_FLASH_PAGE_SIZE;
            pBuffer += SPI_FLASH_PAGE_SIZE;
        }

        SPI_FLASH_PageWrite(pBuffer, Addr, NumOfSingle); //写不足一页的内容
    }
}

void SPI_FLASH_BufferRead(uint8_t *pBuffer, uint32_t Addr, uint32_t Num)
{
#define TIME_OUT 1000 //单位ms

    if (!Num)
    {
        return;
    }

    uint8_t TxBuf[4] = {0, 0, 0, 0};
    uint8_t *pTxBuf = TxBuf;

    SPI_FLASH_WriteEnable();

    SPI_FLASH_CS_LOW;

    *pTxBuf++ = W25X_ReadData;
    *pTxBuf++ = (Addr >> 16) & 0xff;
    *pTxBuf++ = (Addr >> 8) & 0xff;
    *pTxBuf++ = (Addr >> 0) & 0xff;

    HAL_SPI_Transmit(&hspi2, TxBuf, 4, TIME_OUT);

    HAL_SPI_Receive(&hspi2, pBuffer, Num, TIME_OUT);

    SPI_FLASH_CS_HIGH;

#undef TIME_OUT
}

uint32_t SPI_FLASH_ReadID(void)
{
#define TIME_OUT 1000 //单位ms

    uint8_t TxBuf[4] = {0, 0, 0, 0};
    uint8_t RxBuf[4] = {0, 0, 0, 0};
    uint8_t *pTxBuf = TxBuf;

    SPI_FLASH_WriteEnable();

    SPI_FLASH_CS_LOW;

    *pTxBuf++ = W25X_JedecDeviceID;

    SPI_FLASH_CS_HIGH;

    SPI_FLASH_CS_LOW;

    HAL_SPI_Transmit(&hspi2, TxBuf, 1, TIME_OUT);

    HAL_SPI_Receive(&hspi2, RxBuf, 3, TIME_OUT);

    SPI_FLASH_CS_HIGH;

    return ((RxBuf[0] << 16) | (RxBuf[1] << 8) | RxBuf[2]);

#undef TIME_OUT
}

//进入掉电模式
void SPI_Flash_PowerDown(void)
{
#define TIME_OUT 1000 //单位ms

    uint8_t TxBuf[4] = {0, 0, 0, 0};
    uint8_t *pTxBuf = TxBuf;

    SPI_FLASH_CS_LOW;

    *pTxBuf++ = W25X_PowerDown;

    HAL_SPI_Transmit(&hspi2, TxBuf, 1, TIME_OUT);

    SPI_FLASH_CS_HIGH;

#undef TIME_OUT
}

//唤醒
void SPI_Flash_WAKEUP(void)
{
#define TIME_OUT 1000 //单位ms

    uint8_t TxBuf[4] = {0, 0, 0, 0};
    uint8_t *pTxBuf = TxBuf;

    SPI_FLASH_CS_LOW;

    *pTxBuf++ = W25X_ReleasePowerDown;

    HAL_SPI_Transmit(&hspi2, TxBuf, 1, TIME_OUT);

    SPI_FLASH_CS_HIGH;

#undef TIME_OUT
}

void TestFlash(void)
{
    // #define Test_Flash_Mode

    uint8_t Test_Flash_Tx[256] = {0};
    uint8_t Test_Flash_Rx[256] = {0};
    uint32_t Test_Adress = 0x00000000; //0x00100000;

    SPI_FLASH_ChipErase(); //SPI flash全擦除 相当格式化

    debug("Flash ID: 0x%x	\r\n", SPI_FLASH_ReadID()); //测试使用
                                                        /*
	********测试Flash ****************
	(1)先读取，
	(2)然后擦除，
	(3)然后赋值，
	(4)然后读值。
	(5)最后作比较
	*/
                                                        //扇区擦除以前读取数据
#ifdef Test_Flash_Mode
    SPI_FLASH_BufferRead(Test_Flash_Rx, Test_Adress, 256);       //(1)
    debug("The Test Address Begin At %8x	\r\n", Test_Adress); //打印测试地址
    debug("\r\n\r\n	Test_Flash_Rx before Erase	\r\n");

    for (uint16_t i = 0; i < 256; i++)
    {
        if (i % 16 == 0)
        {
            debug("\r\n");
        }
        debug("%2x ", Test_Flash_Rx[i]);
    }
#endif

    SPI_FLASH_SectorErase(Test_Adress); //(2)	块擦除 4096 Byte

#ifdef Test_Flash_Mode
    SPI_FLASH_BufferRead(Test_Flash_Rx, Test_Adress, 256); //

    debug("\r\n\r\n	Test_Flash_Rx after Erase before Write\r\n");

    for (uint16_t i = 0; i < 256; i++)
    {
        if (i % 16 == 0)
        {
            debug("\r\n");
        }
        debug("%2x ", Test_Flash_Rx[i]);
    }
#endif

#ifdef Test_Flash_Mode
    debug("\r\n\r\n	Test_Flash_Tx		\r\n"); //(3)准备赋值
#endif
    for (uint16_t i = 0; i < 256; i++)
    {
#ifdef Test_Flash_Mode
        if (i % 16 == 0)
        {
            debug("\r\n");
        }
#endif

        Test_Flash_Tx[i] = i;

#ifdef Test_Flash_Mode
        debug("%2x ", Test_Flash_Tx[i]);
#endif
    }

    SPI_FLASH_PageWrite(Test_Flash_Tx, Test_Adress, 256); //(3)赋值

#ifdef Test_Flash_Mode
    debug("\r\n\r\n	Test_Flash_Rx Erase	\r\n"); //清零Test_Flash_Rx 准备读值
#endif
    for (uint16_t i = 0; i < 256; i++)
    {
#ifdef Test_Flash_Mode
        if (i % 16 == 0)
        {
            debug("\r\n");
        }
#endif

        Test_Flash_Rx[i] = 0;

#ifdef Test_Flash_Mode
        debug("%2x ", Test_Flash_Rx[i]);
#endif
    }

    SPI_FLASH_BufferRead(Test_Flash_Rx, Test_Adress, 256); //(4)读值

#ifdef Test_Flash_Mode
    debug("\r\n\r\n	Test_Flash_Rx after Write	\r\n");

    for (uint16_t i = 0; i < 256; i++)
    {
        if (i % 16 == 0)
        {
            debug("\r\n");
        }
        debug("%2x ", Test_Flash_Rx[i]);
    }
#endif

    for (uint16_t i = 0; i < 256; i++) //(5) 比较
    {
        if (Test_Flash_Rx[i] != Test_Flash_Tx[i])
        {

            while (1)
            {
                HAL_Delay(1000);
                debug("Test Flash Failure");
            }
        }
    }
    debug("\r\n\r\nTest Flash Success	\r\n"); //测试使用

    SPI_FLASH_ChipErase(); //SPI flash全擦除 相当格式化
}

#include "ff.h"
#include "diskio.h"

void GetDataInSpiFlash(uint32_t sectorAdd, uint32_t sectorNum)
{
    uint8_t buff[FF_MAX_SS];

    for (uint32_t i = 0; i < sectorNum; i++)
    {
        debug("sector is %d\r\n", sectorAdd + i);

        memset(buff, 0, sizeof buff);
        SPI_FLASH_BufferRead(buff, (sectorAdd + i) * 512, FF_MAX_SS);

        for (uint32_t j = 0; j < 512; j++)
        {
            if (j % 16 == 0)
            {
                debug("\r\n");
            }
            debug("%02x ", buff[j]);
        }
        debug("\r\n");
    }
}
